package distributed

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/gorilla/mux"
	clusterapi "github.com/louloulin/dataflare/pkg/cluster"
	"github.com/louloulin/dataflare/pkg/workflow"
)

// Server is a distributed workflow server
type Server struct {
	// engine is the distributed workflow engine
	engine *DistributedEngine
	// clusterManager is the cluster manager
	clusterManager clusterapi.Manager
	// router is the HTTP router
	router *mux.Router
	// server is the HTTP server
	server *http.Server
	// logger is the logger for the server
	logger *log.Logger
	// ctx is the context for the server
	ctx context.Context
	// cancel is the cancel function for the context
	cancel context.CancelFunc
}

// NewServer creates a new distributed workflow server
func NewServer(clusterManager clusterapi.Manager) (*Server, error) {
	// Create context
	ctx, cancel := context.WithCancel(context.Background())

	// Create distributed engine
	engine, err := NewDistributedEngine(clusterManager)
	if err != nil {
		cancel()
		return nil, fmt.Errorf("failed to create distributed engine: %w", err)
	}

	// Create router
	router := mux.NewRouter()

	// Create server
	server := &Server{
		engine:         engine,
		clusterManager: clusterManager,
		router:         router,
		logger:         log.New(os.Stderr, "[DistributedServer] ", log.LstdFlags),
		ctx:            ctx,
		cancel:         cancel,
	}

	// Register routes
	server.registerRoutes()

	return server, nil
}

// Start starts the server
func (s *Server) Start(address string) error {
	s.logger.Printf("Starting distributed workflow server on %s", address)

	// Start distributed engine
	err := s.engine.Start()
	if err != nil {
		return fmt.Errorf("failed to start distributed engine: %w", err)
	}

	// Create HTTP server
	s.server = &http.Server{
		Addr:    address,
		Handler: s.router,
	}

	// Start HTTP server
	go func() {
		if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			s.logger.Printf("HTTP server error: %v", err)
		}
	}()

	s.logger.Printf("Distributed workflow server started successfully")
	return nil
}

// Stop stops the server
func (s *Server) Stop() error {
	s.logger.Printf("Stopping distributed workflow server")

	// Cancel context
	s.cancel()

	// Stop HTTP server
	if s.server != nil {
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		if err := s.server.Shutdown(ctx); err != nil {
			return fmt.Errorf("failed to stop HTTP server: %w", err)
		}
	}

	// Stop distributed engine
	err := s.engine.Stop()
	if err != nil {
		return fmt.Errorf("failed to stop distributed engine: %w", err)
	}

	s.logger.Printf("Distributed workflow server stopped successfully")
	return nil
}

// registerRoutes registers HTTP routes
func (s *Server) registerRoutes() {
	// API routes
	api := s.router.PathPrefix("/api").Subrouter()

	// Workflow routes
	workflows := api.PathPrefix("/workflows").Subrouter()
	workflows.HandleFunc("", s.handleGetWorkflows).Methods("GET")
	workflows.HandleFunc("", s.handleDeployWorkflow).Methods("POST")
	workflows.HandleFunc("/{id}", s.handleGetWorkflow).Methods("GET")
	workflows.HandleFunc("/{id}", s.handleUndeployWorkflow).Methods("DELETE")
	workflows.HandleFunc("/{id}/start", s.handleStartWorkflow).Methods("POST")

	// Run routes
	runs := api.PathPrefix("/runs").Subrouter()
	runs.HandleFunc("", s.handleGetRuns).Methods("GET")
	runs.HandleFunc("/{id}", s.handleGetRun).Methods("GET")
	runs.HandleFunc("/{id}/status", s.handleGetRunStatus).Methods("GET")
	runs.HandleFunc("/{id}/stop", s.handleStopRun).Methods("POST")

	// Cluster routes
	cluster := api.PathPrefix("/cluster").Subrouter()
	cluster.HandleFunc("/nodes", s.handleGetNodes).Methods("GET")
	cluster.HandleFunc("/nodes/{id}", s.handleGetNode).Methods("GET")
	cluster.HandleFunc("/leader", s.handleGetLeader).Methods("GET")
}

// handleGetWorkflows handles GET /api/workflows
func (s *Server) handleGetWorkflows(w http.ResponseWriter, r *http.Request) {
	// Get deployed workflows
	workflows, err := s.engine.GetDeployedWorkflows(r.Context())
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to get workflows: %v", err), http.StatusInternalServerError)
		return
	}

	// Write response
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(workflows)
}

// handleDeployWorkflow handles POST /api/workflows
func (s *Server) handleDeployWorkflow(w http.ResponseWriter, r *http.Request) {
	// Parse workflow definition
	var definition workflow.Definition
	err := json.NewDecoder(r.Body).Decode(&definition)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to parse workflow definition: %v", err), http.StatusBadRequest)
		return
	}

	// Deploy workflow
	workflowID, err := s.engine.DeployWorkflow(r.Context(), &definition)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to deploy workflow: %v", err), http.StatusInternalServerError)
		return
	}

	// Write response
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{
		"id": workflowID,
	})
}

// handleGetWorkflow handles GET /api/workflows/{id}
func (s *Server) handleGetWorkflow(w http.ResponseWriter, r *http.Request) {
	// Get workflow ID
	vars := mux.Vars(r)
	workflowID := vars["id"]

	// Get workflow
	workflow, err := s.engine.GetDeployedWorkflow(r.Context(), workflowID)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to get workflow: %v", err), http.StatusNotFound)
		return
	}

	// Write response
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(workflow)
}

// handleUndeployWorkflow handles DELETE /api/workflows/{id}
func (s *Server) handleUndeployWorkflow(w http.ResponseWriter, r *http.Request) {
	// Get workflow ID
	vars := mux.Vars(r)
	workflowID := vars["id"]

	// Undeploy workflow
	err := s.engine.UndeployWorkflow(r.Context(), workflowID)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to undeploy workflow: %v", err), http.StatusInternalServerError)
		return
	}

	// Write response
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]bool{
		"success": true,
	})
}

// handleStartWorkflow handles POST /api/workflows/{id}/start
func (s *Server) handleStartWorkflow(w http.ResponseWriter, r *http.Request) {
	// Get workflow ID
	vars := mux.Vars(r)
	workflowID := vars["id"]

	// Parse input
	var input map[string]interface{}
	err := json.NewDecoder(r.Body).Decode(&input)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to parse input: %v", err), http.StatusBadRequest)
		return
	}

	// Start workflow
	runID, err := s.engine.StartWorkflow(r.Context(), workflowID, input)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to start workflow: %v", err), http.StatusInternalServerError)
		return
	}

	// Write response
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{
		"id": runID,
	})
}

// handleGetRuns handles GET /api/runs
func (s *Server) handleGetRuns(w http.ResponseWriter, r *http.Request) {
	// Get workflow runs
	runs, err := s.engine.GetWorkflowRuns(r.Context())
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to get runs: %v", err), http.StatusInternalServerError)
		return
	}

	// Write response
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(runs)
}

// handleGetRun handles GET /api/runs/{id}
func (s *Server) handleGetRun(w http.ResponseWriter, r *http.Request) {
	// Get run ID
	vars := mux.Vars(r)
	runID := vars["id"]

	// Get workflow run
	run, err := s.engine.GetWorkflowRun(r.Context(), runID)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to get run: %v", err), http.StatusNotFound)
		return
	}

	// Write response
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(run)
}

// handleGetRunStatus handles GET /api/runs/{id}/status
func (s *Server) handleGetRunStatus(w http.ResponseWriter, r *http.Request) {
	// Get run ID
	vars := mux.Vars(r)
	runID := vars["id"]

	// Get workflow status
	status, err := s.engine.GetWorkflowStatus(r.Context(), runID)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to get run status: %v", err), http.StatusNotFound)
		return
	}

	// Write response
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(status)
}

// handleStopRun handles POST /api/runs/{id}/stop
func (s *Server) handleStopRun(w http.ResponseWriter, r *http.Request) {
	// Get run ID
	vars := mux.Vars(r)
	runID := vars["id"]

	// Stop workflow
	err := s.engine.StopWorkflow(r.Context(), runID)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to stop run: %v", err), http.StatusInternalServerError)
		return
	}

	// Write response
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]bool{
		"success": true,
	})
}

// handleGetNodes handles GET /api/cluster/nodes
func (s *Server) handleGetNodes(w http.ResponseWriter, r *http.Request) {
	// Get cluster nodes
	nodes, err := s.clusterManager.GetNodes(r.Context())
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to get nodes: %v", err), http.StatusInternalServerError)
		return
	}

	// Write response
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(nodes)
}

// handleGetNode handles GET /api/cluster/nodes/{id}
func (s *Server) handleGetNode(w http.ResponseWriter, r *http.Request) {
	// Get node ID
	vars := mux.Vars(r)
	nodeID := vars["id"]

	// Get node
	node, err := s.clusterManager.GetNode(r.Context(), nodeID)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to get node: %v", err), http.StatusNotFound)
		return
	}

	// Write response
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(node)
}

// handleGetLeader handles GET /api/cluster/leader
func (s *Server) handleGetLeader(w http.ResponseWriter, r *http.Request) {
	// Get leader
	leader, err := s.clusterManager.GetLeader(r.Context())
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to get leader: %v", err), http.StatusInternalServerError)
		return
	}

	// Write response
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(leader)
}
